// todo： 将下面的方法 分开 放进项目的 utils 的不同方法下
// 浏览器的指纹
import { createFingerprint } from "./fingerprint.js";
import {formatDateTime } from './utils'
if (window.requestIdleCallback) {
  requestIdleCallback(() => {
    createFingerprint();
  });
} else {
  setTimeout(() => {
    createFingerprint();
  }, 500);
}

// 异步上报数据，不影响页面的渲染
const requestIdleCallbackFun = (data) => {
  if (window.requestIdleCallback) {
    window.requestIdleCallback(() => {
      postData(data);
    });
  } else {
    setTimeout(() => {
      postData(data);
    }, 0);
  }
};

// 获取performance的数据内容
function getPerformanceEntries() {
  // 获取所有内容
  var p = performance.getEntries();
  // 单独处理第一条， 页面的PerformanceNavigationTiming 信息
  let PerformanceTiming = p?.[0];
  let performanceData = {};
  let resourceData = [];
  if (p?.[0]) {
    performanceData.domComplete = PerformanceTiming.domComplete;
    performanceData.duration = PerformanceTiming.duration;
    performanceData.responseEnd = PerformanceTiming.responseEnd;
  } else {
    performanceData.domComplete =
      performance.timing.domComplete - performance.timing.navigationStart;
  }
  // 遍历 内容拿出数据

  baseMsg().then((res) => {
    for (var i = 1; i < p.length; i++) {
      let propertyItem = print_PerformanceEntry(p[i]);
      resourceData.push({ ...propertyItem, ...res, type: "resources" });
    }
    performanceData = { ...performanceData, ...res, type: "performance" };

    requestIdleCallbackFun({
      type: "performance",
      performanceData,
    });
    requestIdleCallbackFun({
      type: "resources",
      resourceData,
    });
  });
}

// 将performanceResourceTiming 里面的值拿出来组成对象
function print_PerformanceEntry(perfEntry) {
  // 先只拿这几个属性
  var properties = ["name", "entryType", "startTime", "duration"];
  let propertyItem = {};
  for (var i = 0; i < properties.length; i++) {
    let hasProperty = properties[i] in perfEntry;
    if (hasProperty) {
      propertyItem[properties[i]] = perfEntry[properties[i]];
    } else {
      console.log("... " + properties[i] + " is NOT supported");
    }
  }
  propertyItem.usedPageName = window.location.href;
  propertyItem.resourceName = perfEntry['name']
  return propertyItem;
}

window.onload = () => {
  getPerformanceEntries();
};

// js error
// window.onerror = (url, msg, row, col) => {
//   console.log('js error', url, msg, row, col);
//   monitor.errors.push({
//       type: 'onerror',
//       reson: {
//         baseURI: url,
//         src: '',
//         href: '',
//         msg: msg,
//         row,
//         col
//       },
//   });
// };

// 资源加载错误
window.addEventListener(
  "error",
  (e) => {
    console.log("js error 错误", e);
    baseMsg().then((res) => {
      let errorsData = {
        type: "error",
        errorData: { ...formatErrMsg(e), ...res },
      };
      requestIdleCallbackFun(errorsData);
    });
  },
  true
);

// promise 内的错误
window.addEventListener("unhandledrejection", (e) => {
  console.log(" promise 内的错误", e);
  baseMsg().then((res) => {
    let errorsData = {
      type: "unhandledrejection",
      errorData: { ...formatErrMsg(e), ...res },
    };
    requestIdleCallbackFun(errorsData);
  });
});

// ajax 的错误 暂时无法捕捉，只能在ajax 、 fetch 的 catch 里面 自定义上报

// 报错信息的数据格式化统一
const formatErrMsg = (e) => {
  let obj = {
    type: "",
    message: "",
    stack: "",
    resourceName: "",
    usedPageName: "",
    row: "",
    col: "",
  };
  // promise 内的错误
  // js error

  // 资源加载错误 和 js error
  // todo： 捕捉到错误行列数 不准确，需要进一步分析原因 做准确的上报
  if (e.type === "error") {
    obj.type = e.type;
    obj.message = e.message || "资源加载错误";
    obj.stack = e.error?.stack || "";
    obj.usedPageName = e.target.localName
      ? judgeElementType(e.target.localName, e)?.usedPageName
      : e.filename || window.location.href;
    obj.resourceName = e.target.localName
      ? judgeElementType(e.target.localName, e)?.resourceName
      : "";
    obj.row = e.lineno;
    obj.col = e.colno;
  } else if (e.type === "unhandledrejection") {
    let stackMsg = e.reason.stack.split(/[(\r\n)\r\n]+/);
    let usedPageName = stackMsg?.[1] ||  window.location.href;
    obj.type = e.type;
    obj.message = e.reason.message;
    obj.stack = e.reason?.stack;
    // 解析出文件名和出错的位置
    obj.usedPageName = usedPageName;
    obj.row = "";
    obj.col = "";
  }
  return obj;
};

// 判断resource的不同元素类型
const judgeElementType = (localName, e) => {
  if (localName === "link") {
    return {
      usedPageName: e?.target?.baseURI,
      resourceName: e?.target?.href,
    };
  }
  if (localName === "script") {
    return {
      usedPageName: e?.target?.baseURI,
      resourceName: e?.target?.src,
    };
  }
  if (localName === "img") {
    return {
      usedPageName: e?.target?.baseURI,
      resourceName: e?.target?.src,
    };
  }
};

// 基本信息 用户信息 时间 浏览器指纹等
const baseMsg = async () => {
  // todo: 当前页面的pageId 如何拿到？ 可以根据pageId 查询该页面的内容 性能以及错误
  const id = window._$tw?.getBrowserId || (await createFingerprint());
  const time = formatDateTime(new Date())
  const userAgent = navigator?.userAgent;
  // 同域名下 基本信息
  return {
    time,
    userAgent,
    browserid: id,
  };
};
// 当前环境的判断
const judgeEnv = () => {
  const hostname = window.location.hostname;
  if (hostname === "localhost") {
    return "dev";
  } else if (hostname.indexOf(".uat") > -1) {
    return "uat";
  } else if (hostname.indexOf(".fat") > -1) {
    return "fat";
  } else {
    return "pro";
  }
};
// 调用接口地址判断
const domainByEnv = () => {
  const env = judgeEnv();
  let domain = "";
  switch (env) {
    case "dev":
      domain = "http://api.itodd.wang/nodejsapi/";
      break;
    case "uat":
      domain = "http://api.uat.itodd.wang/nodejsapi/";
      break;
    case "fat":
      domain = "http://api.fat.itodd.wang/nodejsapi/";
      break;
    case "pro":
      domain = "http://api.itodd.wang/nodejsapi/";
      break;
    default:
      domain = "http://api.itodd.wang/nodejsapi/";
      break;
  }
  return domain;
};

// 定义不同类型的上报数据
// ["error", "resources", "unhandledrejection", "performance", "custome"];
// 发送请求
const postData = (data) => {
  let params = "";
  if (data.type === "error") {
    params = `error=${JSON.stringify(data)}`;
  } else if (data.type === "resources") {
    params = `resources=${JSON.stringify(data)}`;
  } else if (data.type === "unhandledrejection") {
    params = `unhandledrejection=${JSON.stringify(data)}`;
  } else if (data.type === "performance") {
    params = `performance=${JSON.stringify(data)}`;
  } else if (data.type === "custome") {
    params = `custome=${JSON.stringify(data)}`;
  }
  new Image().src = `${domainByEnv()}get/log/trace.gif?${params}`;
};

// 暴露出一个全局的埋点方法
window.errorPush = function (data) {
  let obj = {
    type: data.type || "custome",
    message: data.messgae || "未知",
    stack: "",
    usedPageName: data.url || "",
  };
  baseMsg().then((res) => {
    let errorsData = {
      type: "custome",
      errorData: { ...obj, ...res },
    };
    requestIdleCallbackFun(errorsData);
  });
};
// todo： 各种类型的提交埋点的方法


